package com.example.ai.controller;

import com.example.ai.repository.impl.RedisChatHistoryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.util.MimeType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
import org.springframework.ai.model.Media;
import org.springframework.util.MimeType;
import java.util.List;
import java.util.Objects;

import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;

@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {

    private final ChatClient chatClient;

    private final RedisChatHistoryRepository chatHistoryRepository;
    //如果要使用基于内存的对话记忆，要改成 InMemoryChatHistoryRepository

    // 请求方式和路径不要改动，将来要与前端联调
    @RequestMapping(value = "/chat", produces = "text/html; charset=utf-8")
    public Flux<String> chatStream(@RequestParam(name = "prompt", defaultValue = "讲个笑话") String prompt,
                                   @RequestParam(name = "chatId") String chatId,
                                   @RequestParam(name = "files", required = false) List<MultipartFile> files) {
        // 1.保存会话ID
        chatHistoryRepository.save("chat", chatId);

        if (files == null || files.isEmpty()) {//纯文本聊天
            return textChat(prompt, chatId);

        } else {//多模态
            return multiModalChat(prompt, chatId, files);
        }
    }

    private Flux<String> textChat(String prompt, String chatId) {
        return chatClient
                .prompt(prompt) // 传入user提示词
                .advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId))
                .stream() // 流式输出
                .content(); //返回响应内容
    }

    /**
     * 处理多模态聊天请求的方法
     * 该方法接收用户提示文本、聊天ID和多媒体文件列表，将这些信息发送给聊天模型，并接收模型的响应
     * 主要功能包括解析多媒体文件和构建用户提示，最后流式获取模型的内容响应
     *
     * @param prompt 用户输入的提示文本，用于构建聊天请求的一部分
     * @param chatId 聊天的唯一标识ID，用于跟踪和管理聊天会话
     * @param files 用户上传的多媒体文件列表，可以为空如果用户没有上传文件
     * @return 返回一个Flux流，包含模型响应的内容
     */
    private Flux<String> multiModalChat(String prompt, String chatId, List<MultipartFile> files) {
        //解析多媒体
        // 1.解析多媒体
        List<Media> medias = files.stream()
                .map(file -> new Media(
                                MimeType.valueOf(Objects.requireNonNull(file.getContentType())),
                                file.getResource()
                        )
                )
                .toList();
        //请求模型
        return chatClient.prompt()
                .user(p -> p.text(prompt).media(medias.toArray(Media[]::new)))
                .advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId))
                .stream()
                .content();
    }
}